#include "54func.h"
#include "clientFunc.h"

#define COMMANDNUM 12
#define USERLEN 19

#define QUITIDX 0
#define LOGINIDX 8
#define USERADDIDX 10
#define USERDELIDX 11

typedef struct argc_s{
    char *commandName;
    int min;
    int max;
}argc_t;

// 返回值为0时, 指令和参数个数是正确的, 将cmd发送给服务端
int parseToken(int sockfd, char *command, cmd_t * pcmd){
    bzero(pcmd,sizeof(cmd_t));
    char *token = strtok(command," ");
    if(token == NULL){
        return -1;
    }
    // 存储指令
    strcpy(pcmd->cmdToken,token);
    // 初始化指令信息结构体数组
    argc_t argc[COMMANDNUM]= {
        {"quit",0,0},
        {"pwd",0,0},
        {"cd",0,1},
        {"ls",0,1},
        {"rm",1,1},
        {"mkdir",1,1},
        {"puts",1,2},
        {"gets",1,2},
        {"login",2,2},
        {"logout",0,0},
        {"useradd",2,2},
        {"userdel",2,2}
    };
    argc_t *argcInfo = NULL;
    // 0: 输入的命令不符 1: OK   
    int found = 0;

    // 判断在当前状态下, 指令是否有效
    if(loginInfo.loginFlag == 0) {
        // 未登录
        if(strcmp(pcmd->cmdToken, "login") == 0) {
            argcInfo = &argc[LOGINIDX];
            found = 1;
        }
        if(strcmp(pcmd->cmdToken, "useradd") == 0) {
            argcInfo = &argc[USERADDIDX];
            found = 1;
        }
        if(strcmp(pcmd->cmdToken, "userdel") == 0) {
            argcInfo = &argc[USERDELIDX];
            found = 1;
        }
        if(strcmp(pcmd->cmdToken, "quit") == 0) {
            argcInfo = &argc[QUITIDX];
            found = 1;
        }
        if(found == 0) {
            printf("请先登录或注册账户!\n");
            return -1;
        } 
    }
    else {
        // 已登录
        memcpy(pcmd->user,loginInfo.userName,USERLEN);
        for(int i = 0; i < COMMANDNUM; ++i) {
            if(strcmp(pcmd->cmdToken, argc[i].commandName) == 0) {
                if(i == LOGINIDX) {
                    printf("已有人登录!\n");
                    return -1;
                }
                found = 1;
                argcInfo = &argc[i];
            }
        }
    }

    // 指令为无效指令, 退出
    if(!found){
        printf("指令无效!\n");
        return -1;
    }

    int argNum = 0;
    // 将参数存入pcmd的argv中
    if((token = strtok(NULL, " ")) != NULL) {
        memcpy(pcmd->argv1, token, strlen(token));
        argNum++;
    }
    if((token = strtok(NULL, " ")) != NULL) {
        memcpy(pcmd->argv2, token, strlen(token));
        argNum++;
    }
    // 有第三个参数直接报错退出
    if((token = strtok(NULL, " ")) != NULL) {
        printf("%s: 参数不匹配!\n",pcmd->cmdToken);
        return -1;
    }

    // 判断参数个数是否和指令的最大最小参数个数匹配
    if(argNum < argcInfo->min || argNum > argcInfo->max){
        printf("%s: 参数不匹配!\n",pcmd->cmdToken);
        return -1;
    }

    // 去除服务端有关参数结尾的"/"
    int length = 0;
    if(strcmp(pcmd->cmdToken, "cd") == 0 && pcmd->argc == 1) {
        length = strlen(pcmd->argv1);
        if(pcmd->argv1[length-1] == '/') { pcmd->argv1[length-1] = 0; }
    }
    if(strcmp(pcmd->cmdToken, "ls") == 0 && pcmd->argc == 1) {
        length = strlen(pcmd->argv1);
        if(pcmd->argv1[length-1] == '/') { pcmd->argv1[length-1] = 0; }
    }
    if(strcmp(pcmd->cmdToken, "mkdir") == 0) {
        length = strlen(pcmd->argv1);
        if(pcmd->argv1[length-1] == '/') { pcmd->argv1[length-1] = 0; }
    }
    if(strcmp(pcmd->cmdToken, "rm") == 0) {
        printf("是否确认执行删除操作(将会删除目录及下面所有文件)?(y/n):\n");
        char ch;
        //while(1) {
            ch = fgetc(stdin);
            if(ch != 'y' && ch != 'n') {
                printf("输入不正确,指令取消!\n");
                return -1;
            }
            else if(ch == 'n'){
                return -1;
            }
        //}
        printf("%s->",loginInfo.userName);
        length = strlen(pcmd->argv1);
        if(pcmd->argv1[length-1] == '/') { pcmd->argv1[length-1] = 0; }
    }

    // puts和gets时本地路径是否有效,puts参数有效时修改needSha1
    bool pathValid = true;
    bool needSha1 = false;
    if(strcmp(pcmd->cmdToken, "puts") == 0) {
        char realpath[PATH_MAX] = {0};
        realPath(pcmd->argv1, realpath);
        if(pathJudge(realpath, S_IFREG) == 1) {
            // 本地路径合法就修改needSha1
            needSha1 = true;
            // 检查并去除服务端路径结尾的"/"
            if(pcmd->argc == 2){
                length = strlen(pcmd->argv2);
                if(pcmd->argv2[length-1] == '/') { pcmd->argv2[length-1] = 0; }
            }
        }
        else {
            // 路径不合法
            pathValid = false;
        }
    }
    else if(strcmp(pcmd->cmdToken, "gets") == 0) {
        if(pcmd->argc == 2) {
            char realpath[PATH_MAX] = {0};
            realPath(pcmd->argv2, realpath);
            if(pathJudge(realpath, S_IFDIR) == 1) {
                // 检查并去除服务端路径结尾的"/"
                length = strlen(pcmd->argv1);
                if(pcmd->argv1[length-1] == '/') { pcmd->argv1[length-1] = 0; }
            }
            else {
                // 路径不合法
                pathValid = false;
            }
        }
    }
    if(pathValid == false) {
        printf("本地路径无效!\n");
        return -1;
    }

    // 验证用户名是否过长
    if(strcmp(pcmd->cmdToken, "useradd") == 0 || 
       strcmp(pcmd->cmdToken, "login") == 0) {
        // 需要用户名参数的指令
        length = strlen(pcmd->argv1);
        if(length > USERLEN) {
            printf("用户名不正确!\n");
            return -1;
        }
    }
    if(strcmp(pcmd->cmdToken, "userdel") == 0) {
        // 需要用户名参数的指令
        length = strlen(pcmd->argv1);
        if(length > USERLEN) {
            printf("用户名不正确!\n");
            return -1;
        }
        else if(loginInfo.loginFlag == 1 && 
                strcmp(loginInfo.userName,pcmd->user) == 0)
        {
            printf("当前用户已登录,请退出登录后再试!\n");
            return -1;
        }
    }

    // logout,只发送信息给服务端

    pcmd->argc = argNum;
    //发送结构体cmd_t
    train_t train;
    bzero(&train,sizeof(train));
    train.length = sizeof(cmd_t);
    memcpy(train.data,pcmd,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    // quit 直接退出客户端
    if(strcmp(pcmd->cmdToken, "quit") == 0) {
        printf("已关闭客户端!\n");
        exit(0);
    }
    //根据needSha1值确定是否需要调用对应函数
    if(needSha1) {
        sha1Send(sockfd,pcmd->argv1);
    }

    return 0;
}   
